﻿using Kn.DLMS;
using Kn.DLMS.Internal;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WindowsFormsApplication3;
using static Kn.ALICE.GXAliceData;

namespace Kn.ALICE
{
    /// <summary>
    /// SID enumeration.
    /// </summary>
    public enum SID : UInt16
    {
        // CU报告必备信息 (CU reports essential information)
        SID_CUReportEssentialInformation = 0x8110,

        // DE发送必备信息 (DE sends essential information)
        SID_DESendEssentialInformation = 0x0120,

        // DE查询CU信道特征信息 (DE queries CU channel characteristics)
        SID_DEQueryCUChannelCharacteristics = 0x0202,

        // CU响应信道特征信息 (CU responds with channel characteristics)
        SID_CURespondChannelCharacteristics = 0x8202,

        // DE/CU用户数据发送命令 (DE/CU user data send command)
        SID_DECUSendUserDataCommand = 0x0304,

        // DE发送信道配置参数 (DE sends channel configuration parameters)
        SID_DESendChannelConfigurationParameters = 0x0301,

        // DE查询信道配置参数 (DE queries channel configuration parameters)
        SID_DEQueryChannelConfigurationParameters = 0x0310,

        // CU向DE更新信道状态信息 (CU updates channel status information to DE)
        SID_CUUpdateChannelStatusInformation = 0x8401,

        // DE查询CU信道状态信息 (DE queries CU channel status information)
        SID_DEQueryCUChannelStatusInformation = 0x0401,

        // 请求CU非技术信息 (Request CU non-technical information)
        SID_RequestCUNonTechnicalInformation = 0x0502,

        // CU响应非技术信息 (CU responds with non-technical information)
        SID_CURespondNonTechnicalInformation = 0x8502
    }

    public enum StatusCode : byte
    {
        Connected = 0,                          // 已连接(表示已入网)
        PINCodeError = 1,                       // PIN码错误
        NoService = 2,                          // 无服务
        SIMCardDetectionFailed = 3,             // 未获取身份信息（SIM卡检测失败）
        WrongUsernameOrPassword = 4,            // 用户名/密码错误
        ConnectionRefused = 5,                  // 拒绝连接（PDP激活失败）
        ConnectionTimeout = 6,                  // 连接超时
        NetworkRegistrationFailed = 7,          // 未获取到IP地址（网络注册失败）
        ConnectionDisconnected = 8,             // 连接被远端断开
        NoSignal = 9,                           // 无信标
        NoHeartbeat = 10,                       // 无心跳
        OverCapacity = 11,                      // 超容限
        AddressConflict = 12,                   // 地址冲突
        PortOccupied = 13,                      // 端口占用
        ClientOffline = 14,                     // 客户端不在线(表示未入网)
        CacheFull = 15,                         // 缓存满
        UnknownError = 255                      // 未知错误
    }

    public enum BroadcastFlag : byte
    {
        Unicast = 0,    // 单播
        Broadcast = 1   // 广播
    }
    public enum AddressFormat : byte
    {
        BCD = 1,    // BCD格式
        BIN = 2,    // BIN格式
        ASCII = 3   // ASCII格式
    }

    public enum PacketType : byte
    {
        Unspecified = 0,    // 不限定
        TextMessage = 1,    // 文本短消息
        PDUMessage = 2      // PDU短消息
    }

    public class GXALICESettings
    {
        public byte SEQ;

        ///<summary>
        ///Reset frame sequence.
        ///</summary>
        public void ResetFrameSequence()
        {
            SEQ = 1;
        }


        public GXALICESettings()
        {
            ResetFrameSequence();
        }


    }

    sealed class GXAlice
    {
        const UInt16 ALICEFrameSOF = 0x867D;
        const UInt16 ALICEFrameEOF = 0x9E41;
        const UInt16 LinkID = 0x0100;

        /// <summary>
        /// Get HDLC frame for data.
        /// </summary>
        /// <param name="settings">DLMS settings.</param>
        /// <param name="frame">Frame ID. If zero new is generated.</param>
        /// <param name="data">Data to add.</param>
        /// <returns>HDLC frames.</returns>
        /// 
        static public GXByteBuffer bb = new GXByteBuffer();
        public static byte[] GetALICEFrame(GXALICESettings settings, Byte CTRL, GXByteBuffer INFO)
        {
            
            int frameSize, len;
            byte[] primaryAddress, secondaryAddress;
            bb.Clear();
            bb.SetUInt16(ALICEFrameSOF);

            //Frame len.

            UInt16 LEN;
            LEN = (UInt16)(2 + INFO.Size);
            bb.SetUInt16(LEN);
            UInt16 LENC;
            LENC = (UInt16)(65535 - LEN);
            bb.SetUInt16(LENC);
            bb.SetUInt8(settings.SEQ);
            bb.SetUInt8(CTRL);


            bb.Set(INFO);

            //Add  CRC.
            UInt16 crc = GXFCS16.CountFCS16(bb.Data, 6, bb.Size - 6);
            bb.SetUInt16(crc, false);

            // Add EOP
            bb.SetUInt16(ALICEFrameEOF);

            return bb.Array();
        }

        static public GXByteBuffer INFO = new GXByteBuffer();
        internal static byte[] GetCUReportEssentialInformation(GXALICESettings p, GXAliceData8110 HplcSeting)
        {
            INFO.Clear();
            INFO.SetUInt16(LinkID, true);
            INFO.SetUInt16((UInt16)SID.SID_CUReportEssentialInformation, false);
            INFO.SetUInt16(0x001E);
            INFO.SetUInt16(HplcSeting.ALICEVersion);
            INFO.Set(HplcSeting.BaudRateAndParity);
            INFO.SetUInt8(HplcSeting.MissgingTime);
            INFO.SetUInt16(HplcSeting.InterfaceRxBufferLength);
            INFO.SetUInt8(HplcSeting.CUlinkDirection);
            INFO.SetUInt8(HplcSeting.AT);
            INFO.Set(HplcSeting.CUSerialNumber);
            INFO.Set(HplcSeting.MAC);
            INFO.SetUInt8(HplcSeting.IMEILen);
            Byte CTRL = 0x12;//发起标志(CU发起),方向标志(CU发送),需要确认，SEQ=1，末帧标志位(无后续帧)
            return GetALICEFrame(p, CTRL, INFO);
        }


        internal static byte[] GetCUReporCUUpdateChannelStatusInformationSignalCondition(GXALICESettings p, StatusCode CUSignalCondition)
        {
            //GXByteBuffer INFO = new GXByteBuffer();
            INFO.Clear();
            INFO.SetUInt16(LinkID, true);
            INFO.SetUInt16((UInt16)SID.SID_CUUpdateChannelStatusInformation, false);
            INFO.SetUInt16(5);
            INFO.SetUInt16(0x0001);
            INFO.SetUInt16(1);
            INFO.SetUInt8(CUSignalCondition);
            Byte CTRL = 0x12;//发起标志(CU发起),方向标志(CU发送),需要确认，SEQ=1，末帧标志位(无后续帧)
            return GetALICEFrame(p, CTRL, INFO);
        }


        internal static byte[] GetDECUSendUserDataCommand(GXALICESettings p, GXByteBuffer HDLCFrame)
        {
            //GXByteBuffer INFO = new GXByteBuffer();
            INFO.Clear();
            INFO.SetUInt16(LinkID, true);
            INFO.SetUInt16((UInt16)SID.SID_DECUSendUserDataCommand, false);
            UInt16 LEN_P = (UInt16)(9 + HDLCFrame.Size);
            INFO.SetUInt16(LEN_P);
            INFO.SetUInt8(BroadcastFlag.Unicast);
            INFO.SetUInt16(0x0000); //数据包序号
            INFO.SetUInt8(PacketType.Unspecified);       //数据类型
            INFO.SetUInt8(0);//字符集编号
            INFO.SetUInt8(AddressFormat.BCD);//字符集编号
            INFO.SetUInt8(0);//对端地址格式
            INFO.SetUInt16((UInt16)HDLCFrame.Size);
            INFO.Set(HDLCFrame);

            Byte CTRL = 0x10;//CTRL: 00010000 00000001: 发起标志(CU发起),方向标志(CU发送),无需确认，SEQ=1，末帧标志位(无后续帧)
            return GetALICEFrame(p, CTRL, INFO);
        }
    }

    #region Alice解析器
    public class GXAliceParser
    {
        public byte[] Frame { get; set; }
        GXByteBuffer FrameBuf = new GXByteBuffer();
        GXByteBuffer UIFrameBuf = new GXByteBuffer();
        public ushort SOF { get; set; } = 0x867D;  //帧起始标识域，2字节，其值为 867DH。
        public string strSOF { get; set; }

        public ushort LEN { get; set; } = 0;   //帧长度域，2字节，其值为CTRL域和INFO域的字节数的和
        public ushort LENC { get; set; } = 0;  //帧长度校验域，2字节，其值为LEN的取反
        public ushort CTRL { get; set; } = 0;  //帧控制域，2字节，定义遵循
        public byte CTRL_LSB { get; set; } = 0;
        public bool ORG { get; set; }    //0：本次交互由CU发起  1：本次交互由DE发起
        public bool DIR { get; set; }    //0：本次交互由CU通信模块发送  1：本次交互由DE发送
        public bool REQ_ACK { get; set; }// 0：本帧无需响应。   1：本帧要求响应。
        public bool ACK { get; set; }    // 0：本帧非ACK帧      1：本帧是ACK帧
        public byte SEQ { get; set; }    //SEQ：帧序列号 8比特的2进制值，范围为0～255；由双方各自维护。 
        public bool FINAL { get; set; }  //0：有后续帧 1：无后续帧

        public bool IsNotification { get; set; }  //是否是上报

        public GXByteBuffer DATA = new GXByteBuffer();

        public GXAliceData_DICTIONARY DataDic = new GXAliceData_DICTIONARY();

        public GXAliceData Data = new GXAliceData();


        public ushort CS { get; set; }
        public ushort EOF { get; set; }

        public string titleDirCUEU = ""; //蓝色

        public string FrameResult = "解析结果: 未知";
        public string FrameString = "";

        public bool xmlFormat()
        {
            string ret = "";

            try
            {
                ret += UIFrameBuf.GetString(2) + ": 帧起始标识(SOF)\r\n";
                ret += UIFrameBuf.GetString(2) + ": 帧长度域LEN (CTRL域和INFO域的字节数的和), LEN = " + LEN.ToString() + "\r\n";
                ret += UIFrameBuf.GetString(2) + ": 帧长度校验域LENC (LEN的反码)\r\n";
                string strFINAL = FINAL == true ? "1:无后续帧" : "0:有后续帧";
                string strORG = ORG == true ? "发起标志: 1:本次交互由♂♂♂:DE电表发起" : "0:本次交互由♀♀♀:CU通信模块发起";
                string strDIR = DIR == true ? "方向标志 1:DE电表发送=>CU通信模块" : "0:CU通信模块发送=>DE电表";

                titleDirCUEU = DIR == true ? "DE电表==>CU通信模块:" : "CU通信模块==>DE电表";

                string strREQ_ACK = REQ_ACK == true ? "1:本帧要求响应" : "0:本帧无需响应";
                string strACK = ACK == true ? "1:本帧是***ACK帧***" : "0:本帧▪▪▪非ACK帧▪▪▪";
                string strSEQ = SEQ.ToString();
                ret += UIFrameBuf.GetString(2) + ": 帧控制域, SEQ(" + strSEQ + ")," + "FINAL(" + strFINAL + ")," + "ORG(" + strORG + ")" + ", DIR(" + strDIR + ")," + "REQ_ACK(" + strREQ_ACK + ")," + "ACK(" + strACK + ")\r\n";
                ret += UIFrameBuf.GetString(2) + ": LinkID：" + LinkID.ToString("0000") + "\r\n";
                ret += UIFrameBuf.GetString(2) + ": SID:" + "***<" +"0x" +SID.ToString("X4").Insert(2,",0x") + ">***" + "\r\n";
                ret += UIFrameBuf.GetString(2) + ": LEN_P：DATA子域的字节数:" + LEN_P.ToString() + "字节\r\n";

                if(Data.strUIData != "")
                {
                    ret += Data.strUIData;
                    UIFrameBuf.GetString(LEN - 8);
                }
                else
                {
                    ret += UIFrameBuf.GetString(LEN - 8) + "\r\n";
                }

                ret += UIFrameBuf.GetString(2) + ":CS域(CTRL域及INFO域数据的校验值)\r\n";
                ret += UIFrameBuf.GetString(2) + ": 帧结束标识(EOF)\r\n";

                string cmd = Data.Name;
                if (IsNotification == true)
                {
                    cmd = "▲▲▲数据推送/事件上报▲▲▲";
                }
                else
                {
                    cmd = "★★★" + cmd + "★★★";
                }
                FrameResult =  cmd + "\r\n" + ret;
            }
            catch (Exception ex)
            {
                return false;
            }

            return true;
        }


        public ushort LinkID { get; set; } = 0;
        public ushort SID { get; set; } = 0;

        public ushort LEN_P { get; set; } = 0;

        GXByteBuffer g_tmp = new GXByteBuffer();
        public bool Parser(byte[] HDLCFrame)
        {
            bool result = true;
            try
            {
                FrameBuf.Clear();
                UIFrameBuf.Clear();
                FrameBuf.Set(HDLCFrame);
                UIFrameBuf.Set(HDLCFrame);

                g_tmp.Clear();
                g_tmp.Set(HDLCFrame);
                FrameString = g_tmp.GetString(g_tmp.Size);

                SOF = FrameBuf.GetUInt16();

                LEN = FrameBuf.GetUInt16();
                LENC = FrameBuf.GetUInt16();
                SEQ = FrameBuf.GetUInt8();
                CTRL_LSB = FrameBuf.GetUInt8();

                ACK = (CTRL_LSB & 0x01) != 0;
                REQ_ACK = (CTRL_LSB & 0x02) != 0;
                DIR = (CTRL_LSB & 0x04) != 0;
                ORG = (CTRL_LSB & 0x08) != 0;
                FINAL = (CTRL_LSB & 0x10) != 0;

                LinkID = FrameBuf.GetUInt16();
                SID = FrameBuf.GetUInt16BigEndian();
                LEN_P = FrameBuf.GetUInt16();

                IsNotification = false;
                if((ORG == true) && (SID == 0x0304) && (DIR == true))
                {
                    IsNotification = true;  //上报
                }

                DATA.Clear();
                for (int i = 0; i < LEN_P; i++)
                {
                    DATA.SetUInt8(FrameBuf.GetUInt8());
                }

                if (DataDic.AliceDataDictionary.Keys.Contains(SID))
                {
                    if( ( ( (ACK ==true) && (ORG != true) )  || ( (ACK == true) && (ORG == true) )) && (SID !=0x8502)) //本帧是确认帧
                    {
                        Data = DataDic.AliceDataDictionary[0xFFFF];
                    }
                    else
                    {
                        Data = DataDic.AliceDataDictionary[SID];
                    }
                    Data.IsAck = ACK;
                    Data.Buff.Set(DATA);
                    Data.Parser();
                }
                else
                {
                    result = false;//未知SID
                }
               
                CS = FrameBuf.GetUInt16();
                EOF = FrameBuf.GetUInt16();



                result = xmlFormat();
            }
            catch(Exception ex)
            {
                throw (ex);
            }

            return result;
        }
    }

    public class GXAliceINFOParser
    {
    }

    #endregion

    #region GXHPLCClient类
    public class GXHPLCClient
    {

        public GXAliceData8110 HPLCSeting
        {
            get;
            private set;
        }

        public GXALICESettings Settings
        {
            get;
            private set;
        }


        public GXHPLCClient(GXAliceData8110 InputHPLCSeting)
        {
            HPLCSeting = InputHPLCSeting;
            Settings = new GXALICESettings();
        }

        public GXHPLCClient()
        {
            Settings = new GXALICESettings();
        }

        public void GXSeting(GXAliceData8110 InputHPLCSeting)
        {
            HPLCSeting = InputHPLCSeting;
        }

        public byte[] Report()
        {
            return GXAlice.GetCUReportEssentialInformation(Settings, HPLCSeting);
        }

        public byte[] ReportResponse()
        {
            return GXAlice.GetCUReportEssentialInformation(Settings, HPLCSeting);
        }

        public byte[] Heartbeat()
        {
            return GXAlice.GetCUReporCUUpdateChannelStatusInformationSignalCondition(Settings, StatusCode.Connected);
        }

        public byte[] UserDataSend(GXByteBuffer HDLCFrame)
        {
            return GXAlice.GetDECUSendUserDataCommand(Settings, HDLCFrame);
        }


        public bool Parser(byte[] HDLCFrame, GXAliceParser reply)
        {
            try
            {
                bool result = false;
                try
                {
                    result = reply.Parser(HDLCFrame);
                }
                catch (Exception ex)
                {
                    throw (ex);
                }
                return (result);
            }
            catch(Exception ex)
            {
                throw (ex);
                return false;
            }


        }

    }
    #endregion

    #region GXAliceCommunicator类
    public class GXAliceCommunicator
    {
        internal GXHPLCClient client;

        public GXAliceCommunicator(IGXMedia media, GXAliceData8110 HPLCSeting)
        {
            client = new GXHPLCClient(HPLCSeting);
        }

        public void Report()
        {
            byte[] tmp = client.Report();
            //执行报备
        }


    }
    #endregion

    #region IGXMedia类
    public class IGXMedia
    {
        public CommProcess serial;

        public IGXMedia(CommProcess Inputserial)
        {
            serial = Inputserial;
        }

    }
    #endregion

    #region GXAliceDevice
    public class GXAliceDevice
    {
        public GXAliceCommunicator communicator;  //FOXFOX
        public IGXMedia media;
        public GXAliceDevice(IGXMedia InputMedia, GXAliceData8110 InputHPLCSeting)
        {
            media = InputMedia;
            communicator = new GXAliceCommunicator(InputMedia, InputHPLCSeting);
        }


    }
    #endregion
}
